/*
 * @Author: lizhil
 * @Date: 2020-11-22 19:31:43
 * @Last Modified by: lizhil
 * @Last Modified time: 2024-07-08 22:49:48
 */
const { Controller } = require("egg");
const xlsx = require("node-xlsx");

/**
 * API
 * https://eggjs.org/zh-cn/basics/router.html#restful-%E9%A3%8E%E6%A0%BC%E7%9A%84-url-%E5%AE%9A%E4%B9%89
 */
class BaseController extends Controller {
  // 默认唯一ID
  get key() {
    return "_id";
  }

  // 默认排序
  get sort() {
    return {
      [this.key]: -1,
    };
  }

  // 分页查询
  get isPage() {
    return true;
  }

  // 批量操作
  get batchAction() {
    return true;
  }

  // 过滤器
  get filter() {
    return {};
  }

  // 新增返回数据
  get isNewData() {
    return false;
  }

  // 修改返回数据
  get isUpdateData() {
    return false;
  }

  // 管理员
  get admin() {
    return this.ctx.state.admin;
  }

  // 用户
  get user() {
    return this.ctx.state.user;
  }

  // 过滤条件
  getFilter(options) {
    const { ctx } = this;
    const { query, helper } = ctx;

    const filter = {
      ...this.filter,
      ...options,
    };

    // 匹配查询
    if (this.searchKey) {
      this.searchKey.forEach((key) => {
        if (query[key] != undefined && query[key] != "")
          filter[key] = query[key];
      });
    }

    // 模糊查询
    if (this.likeKey) {
      this.likeKey.forEach((key) => {
        if (query[key] != undefined && query[key] != "") {
          const keyword = query[key].trim();
          const reg = new RegExp(helper.toMongoRegex(keyword), "i"); // i 不区分大小写
          filter[key] = reg;
        }
      });
    }

    return filter;
  }

  // 列表接口
  async index() {
    this.isIndex = true;

    const { ctx } = this;
    const { query, helper } = ctx;

    let options = {
      sort: this.sort,
    };

    // 列表查询前
    if (this.beforeIndex) options = await this.beforeIndex();

    options = {
      ...options,
      ...this.findOptions,
    };

    const filter = this.getFilter(options.filter);

    let returnData;
    let list;

    const isPage = this.isPage || this.findPage;

    // 分页查询
    if (isPage) {
      if (query.page && query.page != "") options.page = Number(query.page);
      if (query.pageSize && query.pageSize != "")
        options.pageSize = Number(query.pageSize);

      returnData = await this.Model.findPage(filter, options);

      list = returnData.list;
    }
    // 查询所有
    else {
      let find = this.Model.find(filter);

      Object.keys(options).map((key) => {
        if (find[key]) find = find[key](options[key]);
      });

      list = returnData = await find;
    }

    // 列表查询后
    if (this.afterIndex) await this.afterIndex(returnData);

    // 格式化数据，使用formatData会自动将自动执行toJSON
    if (list && this.formatData) {
      const newList = [];
      await helper.queue(list, async (doc, index) => {
        if (doc.toJSON) doc = doc.toJSON();
        const newDoc = await this.formatData(doc);
        newList[index] = newDoc ? newDoc : doc;
      });

      if (newList.length) {
        if (isPage) {
          returnData.list = newList;
        } else {
          returnData = newList;
        }
      }
    }

    if (query.isExport) {
      await this.export(isPage ? returnData.list : returnData);
    } else {
      ctx.body = {
        code: 0,
        msg: "success",
        data: returnData,
      };
    }
  }

  // 详情接口
  async show() {
    this.isShow = true;

    const { ctx } = this;
    const { params } = ctx;

    let filter = {
      [this.key]: params.id,
    };

    if (this.beforeShow) {
      const newFilter = await this.beforeShow(filter);
      if (newFilter) filter = newFilter;
    }

    let findOne = this.Model.findOne(filter);

    let doc = await findOne;

    if (doc && this.afterShow) {
      let newDoc = await this.afterShow(doc);
      if (newDoc) doc = newDoc;
    }

    if (!doc) throw "数据获取失败";

    if (this.formatData) {
      doc = doc.toJSON();
      const newDoc = await this.formatData(doc);
      if (newDoc) doc = newDoc;
    }

    ctx.body = {
      code: 0,
      msg: "success",
      data: doc,
    };
  }

  // 新增接口
  async create() {
    this.isNew = true;

    const { ctx } = this;
    const { body } = ctx.request;

    this.validate();
    this.beforeCreate && (await this.beforeCreate());
    this.beforeSave && (await this.beforeSave());

    const doc = await this.Model.create(body);

    this.afterCreate && (await this.afterCreate(doc));
    this.afterSave && (await this.afterSave(doc));

    let returnData;

    if (this.isNewData) returnData = await this.formatData(doc.toJSON());

    ctx.body = {
      code: 0,
      msg: "操作成功",
      data: returnData,
    };
  }

  // 修改接口
  async update() {
    this.isUpdate = true;

    const { ctx } = this;
    const { params } = ctx;
    const { body } = ctx.request;

    const idList = params.id.split(",");

    this.isBatch = idList.length == 1 ? false : true;

    if (this.isBatch && !this.batchAction) throw "不可批量操作";

    this.validate();
    this.beforeUpdate && (await this.beforeUpdate());
    this.beforeSave && (await this.beforeSave());

    let doc;
    let returnData;
    if (this.isBatch) {
      await this.Model.updateMany(
        {
          [this.key]: {
            $in: idList,
          },
        },
        body
      );
    } else {
      // 创建配置
      doc = await this.Model.findOneAndUpdate(
        {
          [this.key]: params.id,
        },
        body,
        {
          new: true, // 返回更新后的数据
        }
      );

      if (!doc) throw "操作失败";

      if (this.isUpdateData) returnData = await this.formatData(doc.toJSON());
    }

    this.afterUpdate && (await this.afterUpdate(doc));
    this.afterSave && (await this.afterSave(doc));

    ctx.body = {
      code: 0,
      msg: "操作成功",
      data: returnData,
    };
  }

  // 删除接口
  async destroy() {
    const { ctx } = this;
    const { params } = ctx;

    const idList = params.id.split(",");

    this.isBatch = idList.length == 1 ? false : true;

    let filter = {};

    if (this.isBatch) {
      if (!this.batchAction) throw "不可批量操作";

      filter = {
        [this.key]: params.id,
      };
    } else {
      filter = {
        [this.key]: {
          $in: idList,
        },
      };
    }

    if (this.beforeDestroy) {
      let fnData = await this.beforeDestroy(filter);
      if (fnData) filter = fnData;
    }

    let returnData;
    if (this.isBatch) {
      await this.Model.deleteMany(filter);
    } else {
      await this.Model.deleteOne(filter);
    }

    this.afterDestroy && (await this.afterDestroy(returnData));

    ctx.body = {
      code: 0,
      msg: "操作成功",
      data: returnData,
    };
  }

  // 数据复制
  async copy() {
    const { ctx } = this;
    const { body } = ctx.request;
    const { id } = body;

    const doc = await this.Model.findOne({ _id: id });
    const newDoc = doc.toJSON();

    // 重命名字段
    if (body.renames) {
      body.renames.forEach((key) => {
        newDoc[key] += "_复制";
      });
    }

    // 过滤字段
    [...(body?.filters || []), "id", "_id"].forEach((key) => {
      delete newDoc[key];
    });

    await this.Model.create(newDoc);

    ctx.body = {
      code: 0,
      msg: "success",
    };
  }

  /**
   * 导出
   */
  async export(data) {
    const { ctx } = this;

    // 导出结构
    if (!this.exportConfig) throw "未配置导出内容";
    const exportConfig = await this.exportConfig(data);

    // 导出头
    const exportHead = [];
    exportConfig.map((item) => {
      exportHead.push(item.name);
    });

    const exportContent = [];

    // 导出内容
    for (let i = 0; i < data.length; i++) {
      let item = data[i];
      if (item.toJSON) item = item.toJSON();

      const exportItem = [];

      for (let i = 0; i < exportConfig.length; i++) {
        const exportConfigItem = exportConfig[i];
        let value;

        if (exportConfigItem.value) {
          value = await exportConfigItem.value(item);
        } else {
          const key = exportConfigItem.key;
          value = exportConfigItem.value || item[key];
        }

        if (value) {
          // 对象转字符串
          if (typeof value == "object") {
            try {
              value = JSON.stringify(value);
            } catch (err) {
              console.log("err", err);
            }
          }
        } else if (value == null || value == undefined) {
          value == "";
        }

        exportItem.push(value);
      }

      exportContent.push(exportItem);
    }

    const buffer = this._buildXlsx([exportHead, ...exportContent]);

    ctx.attachment(Date.now() + ".xlsx");
    ctx.body = buffer;
    // ctx.body = fs.createReadStream(result.filename);
  }

  async exportConfig(data) {
    const config = [];

    for (const key in data) {
      config.push({
        name: key,
        value: key,
      });
    }

    return config;
  }

  _buildXlsx(data) {
    const buffer = xlsx.build([
      {
        name: "sheetName",
        data,
      },
    ]);

    return buffer;
  }

  validate() {
    const { ctx } = this;
    const { helper } = ctx;
    const { body } = ctx.request;

    let data = [];

    const require = this.require || this.requires || this.required;

    if (require) {
      if (this.isNew) {
        data = require;
      } else {
        for (const key in body) {
          if (require.includes(key)) data.push(key);
        }
      }
    }

    helper.validate(body, {
      include: this.include,
      require: data,
    });
  }

  toJson(item, json) {
    if (this.json) {
      const { ctx } = this;
      const { helper } = ctx;

      let newItem = helper.include(item, json || this.json);

      return newItem;
    } else {
      return item;
    }
  }

  // 格式化
  async formatData(item) {
    if (this.toJson) return this.toJson(item);
    else item;
  }
}
module.exports = BaseController;
